#include "Metgine/Core/ResourceManager.hpp"
#include "Metgine/Core/Log.hpp"

namespace Metgine
{
namespace Core
{

void ResourceLoaderThread::Run()
{
    GlobalMutex.Lock();

    ResourceManager* resourceManager = ResourceManager::Get();

    while ((resourceManager->m_loadQueue.size() > 0))
    {
        QueuedResource& resource = resourceManager->m_loadQueue.front();

        switch (resource.type)
        {

        case ResourceType::Image:
            resourceManager->AddResource(resource.name, new ImageResource(resource.path));
            break;

        case ResourceType::Sound:
            resourceManager->AddResource(resource.name, new SoundResource(resource.path));
            break;

        case ResourceType::Font:
            resourceManager->AddResource(resource.name, new FontResource(resource.path));
            break;

        case ResourceType::Text:
            resourceManager->AddResource(resource.name, new TextResource(resource.path));
            break;

        }

        resourceManager->m_loadQueue.pop();
    }

    GlobalMutex.Unlock();
}

ResourceManager::ResourceManager() :
    m_loading(false),
    m_loadThread(new ResourceLoaderThread())
{
}

ResourceManager::~ResourceManager()
{
    FreeAllResources();
    Log::Get()->Write(LogType::Message, "Freed resources");
}

Resource* ResourceManager::GetResource(const std::string& name)
{

    std::map<std::string, Resource*>::iterator i = m_resources.find(name);
    if (i == m_resources.end());
        return NULL;

    return i->second;

}

ImageResource* ResourceManager::GetImage(const std::string& name)
{

    std::map<std::string, Resource*>::iterator i = m_resources.find(name);

    if (i != m_resources.end() && i->second->GetType() == ResourceType::Image)
        return (ImageResource*)i->second;
    else
    {
        Log::Get()->Write(LogType::Warning, "Could not find Image [%s]", name.c_str());
        return NULL;
    }
}


SoundResource* ResourceManager::GetSound(const std::string& name)
{

    std::map<std::string, Resource*>::iterator i = m_resources.find(name);

    if (i != m_resources.end() && i->second->GetType() == ResourceType::Sound)
        return (SoundResource*)i->second;
    else
    {
        Log::Get()->Write(LogType::Warning,  "Could not find Sound [%s]", name.c_str());
        return NULL;
    }
}

FontResource* ResourceManager::GetFont(const std::string& name)
{

    std::map<std::string, Resource*>::iterator i = m_resources.find(name);

    if (i != m_resources.end() && i->second->GetType() == ResourceType::Font)
        return (FontResource*)i->second;
    else
    {
        Log::Get()->Write(LogType::Warning, "Could not find Font [%s]", name.c_str());
        return NULL;
    }
}

TextResource* ResourceManager::GetText(const std::string& name)
{
    std::map<std::string, Resource*>::iterator i = m_resources.find(name);

    if (i != m_resources.end() && i->second->GetType() == ResourceType::Text)
        return (TextResource*)i->second;
    else
    {
        Log::Get()->Write(LogType::Warning, "Could not find Text [%s]", name.c_str());
        return NULL;
    }
}

void ResourceManager::AddResource(const std::string& name, Resource* resource)
{
    sf::Lock lock(GlobalMutex);
    std::map<std::string, Resource*>::iterator i = m_resources.find(name);

    if (i != m_resources.end())
    {
        Log::Get()->Write(LogType::Warning, "Attempt to load resource with duplicate identifier! [%s]", name.c_str());
        return;
    }

    // Only add the resource if it doesn't already exist
    if (i == m_resources.end())
        m_resources.insert(std::pair<std::string, Resource*>(name, resource));
}
void ResourceManager::QueueResource(const std::string& name, ResourceType::ResourceType type, const std::string& path)
{

    QueuedResource q;
    q.name = name;
    q.type = type;
    q.path = path;

    sf::Lock lock(GlobalMutex);
    m_loadQueue.push(q);

}

void ResourceManager::LoadQueuedResources()
{
    if (!m_loading)
    {
        m_loading = true;
        m_loadThread->Launch();
        m_loading = false;
    }
}

void ResourceManager::FreeResource(const std::string& name)
{

    std::map<std::string, Resource*>::iterator i = m_resources.find(name);

    if(i != m_resources.end())
        m_resources.erase(i);

}

void ResourceManager::FreeAllResources()
{

    // Ensure we arent loading queued resources
    while (m_loading)
    {
        GlobalMutex.Unlock();
        sf::Sleep(10);
    }

    // Free all resources
    for (std::map<std::string, Resource*>::iterator i = m_resources.begin(); i != m_resources.end(); ++ i)
        delete i->second;

    m_resources.clear();

}

int ResourceManager::NumResources() const
{

    return m_resources.size();

}

bool ResourceManager::IsLoading() const
{
    return m_loading;
}

}; // Core
}; // Metgine
